﻿using System;
using System.Linq;
using System.Web;
using System.Security.Principal;
using System.Collections;

using Fuse8.DomainFramework.Common;
using Fuse8.DomainFramework.BusinessService;

using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.BizFacade.Services;
using Fuse8.ContentManagementFramework.SystemFacade.Exceptions;
using Fuse8.ContentManagementFramework.SystemFacade;

namespace Fuse8.ContentManagementFramework.Web.ApplicationRuntime
{
    public class DefaultCmsContext : ICmsContext
    {
        #region Consts

        private static readonly string CMS_SITE = "CmsSite";
        private static readonly string CMS_USER = "CmsUser";
        private static readonly string CMS_PAGE = "CmsPage";

        private static readonly string CONTEXT_CMS_SITE_ID = "ContextCmsSiteId";

        #endregion

        #region Fields

        private CmsSiteService _siteService = new CmsSiteService();

        private CmsUserInformationService _userService = new CmsUserInformationService();

        private CmsPageService _pageService = new CmsPageService();

        private HttpContextBase _currentContext = null;

        #endregion

        public DefaultCmsContext()
        {
            UseBuiltInFlow = true;
        }

        #region Properties

        protected CmsSiteService SiteService { get { return _siteService; } }

        public virtual HttpContextBase CurrentContext
        {
            get
            {
                return _currentContext == null ?
                    new HttpContextWrapper(HttpContext.Current) :
                    _currentContext;
            }
            set
            {
                _currentContext = value;
            }
        }

        #endregion

        #region ICmsContext Members

        public virtual CmsSite ContextSite
        {
            get
            {
                CmsSite result = null;

                var context = CurrentContext;

                // see if there is an object already in the items cache for the current http request
                if (context.Items.Contains(CMS_SITE))
                {
                    result = context.Items[CMS_SITE] as CmsSite;
                }
                else
                {
                    var contextSiteId = CmsConfiguration.SiteId;
                    if (IsInAdministrationArea)
                    {
                        var contextSiteIdFromMeta = ContextUser.GetMetaValue<int>(CONTEXT_CMS_SITE_ID);
                        var sitesAvailable = SiteService.FindAvailableByUser(ContextUser);

                        if (contextSiteIdFromMeta != 0 && sitesAvailable.Any(e => e.Id == contextSiteIdFromMeta))
                        {
                            contextSiteId = contextSiteIdFromMeta;
                        }
                        else if (sitesAvailable.Any())
                        {
                            contextSiteId = sitesAvailable.FirstOrDefault().Id;
                        }

                        result = _siteService.FindOne(contextSiteId);
                    }
                    else
                    {
                        var hostname = CurrentContext.Request.Headers["Host"];
                        result = SiteService.FindByHostname(hostname) ?? _siteService.FindOne(contextSiteId);
                    }

                    
                    if (result != null)
                    {
                        context.Items.Add(CMS_SITE, result);
                    }
                    else
                    {
                        throw new SiteNotDefinedException();
                    }
                }

                return result;
            }
            set
            {
                var context = CurrentContext;

                if (value != null)
                {
                    if (IsInAdministrationArea)
                        ContextUser.SetMetaValue<int>(CONTEXT_CMS_SITE_ID, value.Id, true);

                    if (context.Items.Contains(CMS_SITE))
                    {
                        context.Items[CMS_SITE] = value;
                    }
                    else
                    {
                        context.Items.Add(CMS_SITE, value);
                    }
                }
            }
        }

        public virtual CmsUser ContextUser
        {
            get
            {
                //Anonimous user
                CmsUser result = new CmsAnonimousUser();

                var context = CurrentContext;

                if (context.Items.Contains(CMS_USER))
                {
                    result = context.Items[CMS_USER] as CmsUser;
                }
                else
                {
                    IPrincipal user = context.User;
                    if ((user != null) && (user.Identity.IsAuthenticated))
                    {
                        var userInformation = _userService.GetUserInformationByLoginName(user.Identity.Name, true);
                        if ((userInformation != null) && (userInformation.LoggedInUser != null))
                        {
                            result = userInformation.LoggedInUser;

                            context.Items.Add(CMS_USER, result);
                        }
                    }
                }

                return result;
            }
        }

        public virtual CmsPage ContextPage
        {
            get
            {
                CmsPage result = null;

                var context = CurrentContext;

                if (context.Items.Contains(CMS_PAGE))
                {
                    result = context.Items[CMS_PAGE] as CmsPage;
                }
                else
                {
                    string url = context.Request.RequestType != "POST" ?
                        context.Request.Url.AbsolutePath :
                        context.Request.UrlReferrer.AbsolutePath;

                    if (!string.IsNullOrEmpty(url))
                    {
                        result = _pageService.GetPageByUrl(url);

                        if (result != null)
                        {
                            result = result.IsCurrentlyPublished ? result : null;
                        }
                    }

                    if (result != null)
                    {
                        context.Items.Add(CMS_PAGE, result);
                    }
                }

                return result;
            }
            set
            {
                var context = CurrentContext;

                if (value != null)
                {
                    if (context.Items.Contains(CMS_PAGE))
                    {
                        context.Items[CMS_PAGE] = value;
                    }
                    else
                    {
                        context.Items.Add(CMS_PAGE, value);
                    }
                }
            }
        }

        public virtual IDictionary ContextItems
        {
            get
            {
                return CurrentContext.Items;
            }
        }

        public virtual bool IsInAdministrationArea
        {
            get 
            {
                return CurrentContext.Request.RawUrl.StartsWith("/admin", StringComparison.OrdinalIgnoreCase);
            }
        }

        public virtual string MapPath(string path)
        {
            Guard.ArgumentNotEmpty(path, "path");

            return CurrentContext.Server.MapPath(path);
        }


        public bool UseBuiltInFlow
        {
            get;
            set;
        }

        public DateTime CurrentDateTime
        {
            get { return DateTime.Now; }
        }

        #endregion

    }
}
